﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using FreeSql;
using FreeSql.Internal;
using Microsoft.Extensions.Logging;

namespace FreeSqlExtensions
{
    #region 扩展方式 1 重写ifreesql https://github.com/dotnetcore/FreeSql/wiki/%e5%88%86%e8%a1%a8%e5%88%86%e5%ba%93#%E5%88%86%E5%BA%931-%E5%88%A9%E7%94%A8-idlebus-%E9%87%8D%E5%86%99-ifreesql

    public class MultiFreeSql : MultiFreeSql<string> { }

    public partial class MultiFreeSql<TDBKey> : IFreeSql
    {
        internal TDBKey _dbkeyMaster;
        internal AsyncLocal<TDBKey> _dbkeyCurrent = new AsyncLocal<TDBKey>();
        internal IFreeSql _ormMaster => _ib.Get(_dbkeyMaster);
        internal IFreeSql _ormCurrent => _ib.Get(object.Equals(_dbkeyCurrent.Value, default(TDBKey)) ? _dbkeyMaster : _dbkeyCurrent.Value);
        internal IdleBus<TDBKey, IFreeSql> _ib;

        public MultiFreeSql()
        {
            _ib = new IdleBus<TDBKey, IFreeSql>();
            _ib.Notice += (_, __) => { };
        }

        public IAdo Ado => _ormCurrent.Ado;
        public IAop Aop => _ormCurrent.Aop;
        public ICodeFirst CodeFirst => _ormCurrent.CodeFirst;
        public IDbFirst DbFirst => _ormCurrent.DbFirst;
        public GlobalFilter GlobalFilter => _ormCurrent.GlobalFilter;
        public void Dispose() => _ib.Dispose();

        public void Transaction(Action handler) => _ormCurrent.Transaction(handler);
        public void Transaction(IsolationLevel isolationLevel, Action handler) => _ormCurrent.Transaction(isolationLevel, handler);

        public ISelect<T1> Select<T1>() where T1 : class => _ormCurrent.Select<T1>();
        public ISelect<T1> Select<T1>(object dywhere) where T1 : class => Select<T1>().WhereDynamic(dywhere);

        public IDelete<T1> Delete<T1>() where T1 : class => _ormCurrent.Delete<T1>();
        public IDelete<T1> Delete<T1>(object dywhere) where T1 : class => Delete<T1>().WhereDynamic(dywhere);

        public IUpdate<T1> Update<T1>() where T1 : class => _ormCurrent.Update<T1>();
        public IUpdate<T1> Update<T1>(object dywhere) where T1 : class => Update<T1>().WhereDynamic(dywhere);

        public IInsert<T1> Insert<T1>() where T1 : class => _ormCurrent.Insert<T1>();
        public IInsert<T1> Insert<T1>(T1 source) where T1 : class => Insert<T1>().AppendData(source);
        public IInsert<T1> Insert<T1>(T1[] source) where T1 : class => Insert<T1>().AppendData(source);
        public IInsert<T1> Insert<T1>(List<T1> source) where T1 : class => Insert<T1>().AppendData(source);
        public IInsert<T1> Insert<T1>(IEnumerable<T1> source) where T1 : class => Insert<T1>().AppendData(source);

        public IInsertOrUpdate<T1> InsertOrUpdate<T1>() where T1 : class => _ormCurrent.InsertOrUpdate<T1>();
    }

    public static class MultiFreeSqlExtensions
    {
        public static IFreeSql Change<TDBKey>(this IFreeSql fsql, TDBKey dbkey)
        {
            var multiFsql = fsql as MultiFreeSql<TDBKey>;
            if (multiFsql == null) throw new Exception("fsql 类型不是 MultiFreeSql<TDBKey>");
            multiFsql._dbkeyCurrent.Value = dbkey;
            return multiFsql;
        }

        public static IFreeSql Register<TDBKey>(this IFreeSql fsql, TDBKey dbkey, Func<IFreeSql> create)
        {
            var multiFsql = fsql as MultiFreeSql<TDBKey>;
            if (multiFsql == null) throw new Exception("fsql 类型不是 MultiFreeSql<TDBKey>");
            if (multiFsql._ib.TryRegister(dbkey, create))
                if (multiFsql._ib.GetKeys().Length == 1)
                    multiFsql._dbkeyMaster = dbkey;
            return multiFsql;
        }
    }

    /*
     //定义和注入
  var fsql = new MultiFreeSql();
fsql.Register("db1", () => new FreeSqlBuilder().UseConnectionString(DataType.MySql, "str1").Build());
fsql.Register("db2", () => new FreeSqlBuilder().UseConnectionString(DataType.MySql, "str2").Build());
fsql.Register("db3", () => new FreeSqlBuilder().UseConnectionString(DataType.SqlServer, "str3").Build());

services.AddSingleton<IFreeSql>(fsql);


    //调用方式：额外增加 Register/Change 两个方法，其他跟 IFreeSql 用法几乎一样

var db1_list = fsql.Select<T>().ToList();

//切换 db2 数据库，一旦切换之后 fsql 操作都是针对 db2
fsql.Change("db2");
var db2_list = fsql.Select<T>().ToList();

//这样也行
var db2_list = fsql.Change("db2").Select<T>().ToList();
     */

    #endregion
    
    #region 扩展方式 2 https://github.com/dotnetcore/FreeSql/wiki/%E5%88%86%E8%A1%A8%E5%88%86%E5%BA%93#%E5%88%86%E5%BA%932-idlebus
    /// <summary>
    /// IdleBus 扩展方法
    /// </summary>
    public static class IdleBusExtesions
    {
        //参考文档
        //https://github.com/dotnetcore/FreeSql/issues/44
        //https://github.com/2881099/IdleBus
        //https://github.com/2881099/FreeSql.Cloud/blob/master/src/FreeSql.Cloud/FreeSqlCloud.cs  这样封装就不需要使用的时候理解 IdleBus 了

        static AsyncLocal<string> AsyncLocalTenantId = new AsyncLocal<string>();

        private const string DefualtKey = "defualt";

        /// <summary>
        /// 初始化注册默认的ib,并添加fsql.aop，此方法只能执行一次
        /// </summary>
        /// <param name="ib"></param>
        /// <param name="key"></param>
        /// <param name="dataType"></param>
        /// <param name="connectionString"></param>
        /// <param name="logger"></param>
        public static void InitRegisterDefaultIb(this IdleBus<IFreeSql> ib, string key,FreeSql.DataType dataType,  string connectionString,ILogger logger)
        {
            /* 注意写法,需要在Register方法内声明ifreesql
var obj = new Xxx();
ib.Register("key01", () => obj); //错了，错了，错了

ib.Register("key01", () => new Xxx()); //正确
             */
            IFreeSql fsql;
            ib.Register(key, () =>
            {
                switch (dataType)
                {
                    default://默认sqlserver
                        fsql= new FreeSql.FreeSqlBuilder()
                            .UseConnectionString(dataType, connectionString)
                            .UseAutoSyncStructure(false) //自动同步实体结构到数据库
                            //.UseMonitorCommand(cmd => Logger.Default.Debug(cmd.CommandText))
                            .UseNoneCommandParameter(true)
                            .Build(); //请务必定义成 Singleton 单例模式
                        break;
                }

                fsql.FreeSqlAop(logger, key);
                return fsql;
            });
        }


        public static IdleBus<IFreeSql> ChangeTenant(this IdleBus<IFreeSql> ib, string tenantId)
        {
            AsyncLocalTenantId.Value = tenantId;
            return ib;
        }

        public static IFreeSql Get(this IdleBus<IFreeSql> ib) => ib.Get(AsyncLocalTenantId.Value ?? DefualtKey);

     

        /*
        static void test()
        {
            IdleBus<IFreeSql> ib = null; //单例注入

            var fsql = ib.Get(); //获取当前租户对应的 IFreeSql

            var fsql00102 = ib.ChangeTenant("00102").Get(); //切换租户，后面的操作都是针对 00102

            var songRepository = ib.GetRepository<Song>();
            var detailRepository = ib.GetRepository<Detail>();
        }

        public static IServiceCollection AddRepository(this IServiceCollection services, params Assembly[] assemblies)
        {
            services.AddScoped(typeof(IBaseRepository<>), typeof(YourDefaultRepository<>));
            services.AddScoped(typeof(BaseRepository<>), typeof(YourDefaultRepository<>));

            services.AddScoped(typeof(IBaseRepository<,>), typeof(YourDefaultRepository<,>));
            services.AddScoped(typeof(BaseRepository<,>), typeof(YourDefaultRepository<,>));

            if (assemblies?.Any() == true)
                foreach (var asse in assemblies)
                foreach (var repo in asse.GetTypes().Where(a => a.IsAbstract == false && typeof(IBaseRepository).IsAssignableFrom(a)))
                    services.AddScoped(repo);

            return services;
        }
        */
    }

    #endregion

}
